package cz.none.bezbarier.web.service.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;

import cz.none.bezbarier.web.service.BaseService;
import cz.none.bezbarier.web.service.exception.ServiceException;

public abstract class BaseServiceImpl<Form, Model, ID extends Serializable> implements
		BaseService<Form, Model, ID> {

	@Autowired
	protected Mapper mapper;

	private Class<Model> entityClazz;
	private Class<Form> formClazz;

	@SuppressWarnings("unchecked")
	public BaseServiceImpl() {
		final ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
		final Type[] typeArguments = genericSuperclass.getActualTypeArguments();
		entityClazz = (Class<Model>) typeArguments[1];
		formClazz = (Class<Form>) typeArguments[0];
	}

	@Override
	@Transactional(readOnly = true)
	public List<Model> findAll() {
		return getRepository().findAll();
	}

	@Override
	@Transactional(readOnly = true)
	public Model find(ID id) throws ServiceException {
		Model model = getRepository().findOne(id);
		if (null == model) {
			throw new ServiceException("Model not found by id: " + id);
		}

		return model;
	}

	@Override
	@Transactional(readOnly = true)
	public Form fillForm(ID id) throws ServiceException {
		return modelToForm(find(id));
	}

	@Override
	public void save(Model model) {
		getRepository().save(model);
	}

	@Override
	public void saveForm(Form form) {
		Model model = formToModel(form);
		model = changeModel(form, model);
		getRepository().save(model);
	}

	@Override
	public void delete(ID id) {
		getRepository().delete(id);
	}

	protected Model changeModel(Form form, Model model) {
		return model;
	}

	protected Model formToModel(Form form) {
		return mapper.map(form, entityClazz);
	}

	protected Form modelToForm(Model model) {
		return mapper.map(model, formClazz);
	}

	protected abstract JpaRepository<Model, ID> getRepository();

}
